/**
 * tester.m
 *
 * This file is part of the MPInteger library
 * Distributed under the GNU General Public License
 *
 * Written by Bill Garrison
 * Last changed 2008 Dec 22
 *
 * This file is used to test the various aspects of the MPInteger library.
 */

#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import "MPInteger/MPInteger.h"
#import "gmp.h"

int failCount;
int totalFail;

void testMPI( MPInteger *a, long b )
{
  if( [a intValue] == b )
    printf( "PASS\n" );
  else
  {
    printf( "FAIL\n" );
    failCount++;
    totalFail++;
  }
}

void testInt( long a, long b )
{
  if( a == b )
    printf( "PASS\n" );
  else
  {
    printf( "FAIL\n" );
    failCount++;
    totalFail++;
  }
}

void testDoub( double a, double b )
{
  if( a == b )
    printf( "PASS\n" );
  else
  {
    printf( "FAIL\n" );
    failCount++;
    totalFail++;
  }
}

void testStr( char *a, char *b )
{
  if( strcmp( a, b) == 0 )
    printf( "PASS\n" );
  else
  {
    printf( "FAIL\n" );
    failCount++;
    totalFail++;
  }
}

void testBool( _Bool a )
{
  if( a )
    printf( "PASS\n" );
  else
  {
    printf( "FAIL\n" );
    failCount++;
    totalFail++;
  }
}

int main( int argc, const char *argv[] )
{
  MPInteger *i1, *i2, *i3, *ans;
  long ansInt = 9000;
  char *ansStr;
  totalFail = 0;
  
  printf( "INITIALIZING:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] init];
  printf( "init\t\t\t" );
  testMPI( i1, 0 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 534];
  i2 = [[MPInteger alloc] initWithMPInteger: i1];
  [i1 free];
  printf( "initWithMPInteger:\t" );
  testMPI( i2, 534 );
  [i2 free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 349];
  printf( "initWithUnsignedInt:\t" );
  testMPI( i1, 349 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "884"];
  printf( "initWithString:\t\t" );
  testMPI( i1, 884 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "0b1010110001011011"];
  printf( "initWithString:\t\t" );
  testMPI( i1, 44123 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "0xAC5b"];
  printf( "initWithString:\t\t" );
  testMPI( i1, 44123 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "FE502A" inBase: 16];
  printf( "initWithString:inBase:\t" );
  testMPI( i1, 16666666 );
  [i1 free];
  
  mpz_t a;
  mpz_init( a );
  mpz_set_si( a, -348 );
  i1 = [[MPInteger alloc] initWithMpz: a];
  printf( "initWithMpz:\t\t" );
  testMPI( i1, -348 );
  mpz_clear( a );
  [i1 free];
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "ARITHMETIC:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 2319];
  ans = [i1 mpIntegerByAddingMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByAddingMPInteger:\t\t" );
  testMPI( ans, 543 + 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  ans = [i1 mpIntegerByAddingUnsignedInt: 2319];
  [i1 free];
  printf( "mpIntegerByAddingUnsignedInt:\t\t" );
  testMPI( ans, 543 + 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 2319];
  ans = [i1 mpIntegerBySubtractingMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerBySubtractingMPInteger:\t" );
  testMPI( ans, 543 - 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  ans = [i1 mpIntegerBySubtractingUnsignedInt: 2319];
  [i1 free];
  printf( "mpIntegerBySubtractingUnsignedInt:\t" );
  testMPI( ans, 543 - 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 2319];
  ans = [i1 mpIntegerByMultiplyingMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByMultiplyingMPInteger:\t" );
  testMPI( ans, 543 * 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  ans = [i1 mpIntegerByMultiplyingUnsignedInt: 2319];
  [i1 free];
  printf( "mpIntegerByMultiplyingUnsignedInt:\t" );
  testMPI( ans, 543 * 2319 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  ans = [i1 mpIntegerByNegating];
  [i1 free];
  printf( "mpIntegerByNegating\t\t\t" );
  testMPI( ans, -543 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  i2 = [i1 mpIntegerByNegating];
  ans = [i2 mpIntegerByNegating];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByNegating\t\t\t" );
  testMPI( ans, 543 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  ans = [i1 mpIntegerByAbsoluteValue];
  [i1 free];
  printf( "mpIntegerByAbsoluteValue\t\t" );
  testMPI( ans, 543 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 543];
  i2 = [i1 mpIntegerByNegating];
  ans = [i2 mpIntegerByAbsoluteValue];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByAbsoluteValue\t\t" );
  testMPI( ans, 543 );
  [ans free];
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "COMPARISON:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] initWithString: "12332165465491414212131"];
  i2 = [[MPInteger alloc] initWithString: "12332165465491414212130"];
  ansInt = [i1 compare: i2];
  [i1 free];
  [i2 free];
  printf( "compare:\t\t" );
  testInt( ansInt, MPIOrderedDescending );
  ansInt = 9000;
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 123];
  i2 = [i1 mpIntegerByNegating];
  ansInt = [i2 compare: i1];
  [i1 free];
  [i2 free];
  printf( "compare:\t\t" );
  testInt( ansInt, MPIOrderedAscending );
  ansInt = 9000;
  
  i1 = [[MPInteger alloc] initWithString: "6543211871002102102154547"];
  i2 = [[MPInteger alloc] initWithString: "6543211871002102102154547"];
  ansInt = [i2 compare: i1];
  [i1 free];
  [i2 free];
  printf( "compare:\t\t" );
  testInt( ansInt, MPIOrderedSame );
  ansInt = 9000;
  
  i1 = [[MPInteger alloc] initWithString: "-6543211871002102102154547"];
  i2 = [[MPInteger alloc] initWithString: "-6543211871002102102154547"];
  ansInt = [i2 isEqualToMPInteger: i1];
  [i1 free];
  [i2 free];
  printf( "isEqualToMPInteger:\t" );
  testInt( ansInt, YES );
  ansInt = 9000;
  
  i1 = [[MPInteger alloc] initWithString: "6543211871002102102154547"];
  i2 = [[MPInteger alloc] initWithString: "6543211871002102102154543"];
  ansInt = [i2 isEqualToMPInteger: i1];
  [i1 free];
  [i2 free];
  printf( "isEqualToMPInteger:\t" );
  testInt( ansInt, NO );
  ansInt = 9000;
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "CONVERSION:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] initWithString: "-6541"];
  printf( "intValue\t\t" );
  testInt( [i1 intValue], -6541 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "879456"];
  printf( "unsignedIntValue\t" );
  testInt( [i1 unsignedIntValue], 879456 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "-789456156"];
  printf( "unsignedIntValue\t" );
  testInt( [i1 unsignedIntValue], 789456156 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "-456112"];
  printf( "doubleValue\t\t" );
  testDoub( [i1 doubleValue], -456112.0 );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "-456112"];
  printf( "stringValue\t\t" );
  ansStr = [i1 stringValue];
  [i1 free];
  testStr( ansStr, "-456112" );
  free( ansStr );
  
  i1 = [[MPInteger alloc] initWithString: "9842313251891816132135464"];
  printf( "stringValue\t\t" );
  ansStr = [i1 stringValue];
  [i1 free];
  testStr( ansStr, "9842313251891816132135464" );
  free( ansStr);
  
  i1 = [[MPInteger alloc] initWithString: "984231325135464"];
  printf( "stringValueInBase:\t" );
  ansStr = [i1 stringValueInBase: 16];
  [i1 free];
  testStr( ansStr, "37f273682f268" );
  free( ansStr);
  
  i1 = [[MPInteger alloc] initWithString: "65412" inBase: 16];
  printf( "stringValueInBase:\t" );
  ansStr = [i1 stringValueInBase: 2];
  [i1 free];
  testStr( ansStr, "1100101010000010010" );
  free( ansStr);
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "DIVISION:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 2090140];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 32156];
  ans = [i1 mpIntegerByDividingMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByDividingMPInteger:\t\t" );
  testMPI( ans, 2090140 / 32156 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithUnsignedInt: 2090140];
  ans = [i1 mpIntegerByDividingUnsignedInt: 32156];
  [i1 free];
  printf( "mpIntegerByDividingUnsignedInt:\t\t" );
  testMPI( ans, 2090140 / 32156 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-2090140"];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 32156];
  ans = [i1 mpIntegerByDividingMPIntegerExactly: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByDividingMPIntegerExactly:\t" );
  testMPI( ans, -2090140 / 32156 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-2090140"];
  ans = [i1 mpIntegerByDividingUnsignedIntExactly: 32156];
  [i1 free];
  printf( "mpIntegerByDividingUnsignedIntExactly:\t" );
  testMPI( ans, -2090140 / 32156 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-52537239720"];
  i2 = [[MPInteger alloc] initWithString: "9875421"];
  ans = [i1 mpIntegerByDividingMPInteger: i2 exactly: YES];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByDividingMPInteger:exactly:\t" );
  testMPI( ans, -5320 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-52537239720"];
  i2 = [[MPInteger alloc] initWithString: "-9875421"];
  ans = [i1 mpIntegerByDividingMPInteger: i2 exactly: NO];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByDividingMPInteger:exactly:\t" );
  testMPI( ans, 5320 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  i2 = [[MPInteger alloc] initWithString: "545412"];
  ans = [i1 mpIntegerByDividingMPInteger: i2 exactly: NO];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByDividingMPInteger:exactly:\t" );
  testMPI( ans, 42395 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-52537239720"];
  ans = [i1 mpIntegerByDividingUnsignedInt: 9875421 exactly: YES];
  [i1 free];
  printf( "mpIntegerByDividingUnsignedInt:exactly:\t" );
  testMPI( ans, -5320 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "52537239720"];
  ans = [i1 mpIntegerByDividingUnsignedInt: 9875421 exactly: NO];
  [i1 free];
  printf( "mpIntegerByDividingUnsignedInt:exactly:\t" );
  testMPI( ans, 5320 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  ans = [i1 mpIntegerByDividingUnsignedInt: 545412 exactly: NO];
  [i1 free];
  printf( "mpIntegerByDividingUnsignedInt:exactly:\t" );
  testMPI( ans, 42395 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  i2 = [[MPInteger alloc] initWithString: "545412"];
  ans = [i1 mpIntegerByModuloMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByModuloMPInteger:\t\t" );
  testMPI( ans, 390824 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  ans = [i1 mpIntegerByModuloUnsignedInt: 545412];
  [i1 free];
  printf( "mpIntegerByModuloUnsignedInt:\t\t" );
  testMPI( ans, 390824 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  i2 = [[MPInteger alloc] initWithString: "545412"];
  printf( "isDivisibleByMPInteger:\t\t\t" );
  testBool( ![i1 isDivisibleByMPInteger: i2] );
  [i1 free];
  [i2 free];
  
  i1 = [[MPInteger alloc] initWithString: "23122741740"];
  i2 = [[MPInteger alloc] initWithString: "545412"];
  printf( "isDivisibleByMPInteger:\t\t\t" );
  testBool( [i1 isDivisibleByMPInteger: i2] );
  [i1 free];
  [i2 free];
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  printf( "isDivisibleByUnsignedInt:\t\t" );
  testBool( ![i1 isDivisibleByUnsignedInt: 545412] );
  [i1 free];
  
  i1 = [[MPInteger alloc] initWithString: "23122741740"];
  printf( "isDivisibleByUnsignedInt:\t\t" );
  testBool( [i1 isDivisibleByUnsignedInt: 545412] );
  [i1 free];
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "EXPONENTIATION:\n" );
  failCount = 0;
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  ans = [i1 mpIntegerByRaisingToExponentUnsignedInt: 54];
  [i1 free];
  ansStr = [ans stringValue];
  [ans free];
  printf( "mpIntegerByRaisingToExponentUnsignedInt:\t\t" );
  testStr( ansStr, "45553329496117015643764134219441699227872903253210864779493216543926346085953683913036755965537414565544980209697361981908141639768664699786434167359755647155031222329812354906069138767021232720100148609019582493217312660223948308981591995735741912418489445310892977912544590921590452549791238876304012217612399427774860340669559281870831241809258606155243165936895673123541488940200346255723766638607893118031096484770633138653781038434287249702117847855750352788326901203148441948001937172096139015959456071178944459119322130527518420143344518943136039305216" );
  free( ansStr );
  
  i1 = [[MPInteger alloc] initWithString: "23123132564"];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 54];
  i3 = [[MPInteger alloc] initWithUnsignedInt: 200];
  ans = [i1 mpIntegerByRaisingToExponentMPInteger: i2
                                     modMPInteger: i3];
  [i1 free];
  [i2 free];
  [i3 free];
  printf( "mpIntegerByRaisingToExponentMPInteger:modMPInteger:\t" );
  testMPI( ans, 16 );
  [ans free];
  
  i1 = [[MPInteger alloc] initWithString: "-23123132564"];
  i2 = [[MPInteger alloc] initWithUnsignedInt: 200];
  ans = [i1 mpIntegerByRaisingToExponentUnsignedInt: 54
                                       modMPInteger: i2];
  [i1 free];
  [i2 free];
  printf( "mpIntegerByRaisingToExponentUnsignedInt:modMPInteger:\t" );
  testInt( [ans intValue], 16 );
  [ans free];
  
  if( failCount != 0 )
    printf( "%i tests failed\n", failCount );
  printf( "\n" );
  
  printf( "SUMMARY:\n" );
  if( totalFail == 0 )
    printf( "All tests passed\n" );
  else
    printf( "%i tests failed\n", totalFail );
  
  return 0;
}

